/**
 * Copyright (C), 2015-2018, XXX有限公司
 * FileName: ShiroConfig
 * Author:   zhouheng
 * Date:     2018/6/17 14:29
 * Description: shior权限配置类
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.hengby.hengcrud.config.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.hengby.hengcrud.config.properties.HengCrudProperties;
import com.hengby.hengcrud.core.shiro.ShiroDbRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.io.ClassPathResource;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 〈一句话功能简述〉<br> 
 * 〈shior权限配置类〉
 *
 * @author zhouheng
 * @create 2018/6/17
 * @since 1.0.0
 */
@Configuration
public class ShiroConfig {

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(CookieRememberMeManager cookieRememberMeManager,
                                                               CacheManager cacheManager,
                                                               SessionManager sessionManager,
                                                               ShiroDbRealm shiroDbRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setSessionManager(sessionManager);
        defaultWebSecurityManager.setCacheManager(cacheManager);
        defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager);

        defaultWebSecurityManager.setRealm(shiroDbRealm);

        return defaultWebSecurityManager;
    }

    @Bean
    public DefaultWebSessionManager getSessionManaer(CacheManager cacheShiroManager,HengCrudProperties hengCrudProperties){
        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();

        defaultWebSessionManager.setCacheManager(cacheShiroManager);
        defaultWebSessionManager.setSessionValidationInterval(hengCrudProperties.getSessionValidationInterval() * 1000);
        defaultWebSessionManager.setGlobalSessionTimeout(hengCrudProperties.getSessionInvalidateTime() * 1000);
        defaultWebSessionManager.setDeleteInvalidSessions(true);
        defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
        Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
        cookie.setName("shiroCookie");
        cookie.setHttpOnly(true);
        defaultWebSessionManager.setSessionIdCookie(cookie);

        return defaultWebSessionManager;
    }

    @Bean
    public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(ehcache.getObject());

//        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        return  ehCacheManager;
    }


    @Bean
    public CookieRememberMeManager cookieRememberMeManager(SimpleCookie simpleCookie){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCipherKey(Base64.decode("abcdddddddddddddddddd"));
        cookieRememberMeManager.setCookie(simpleCookie);

        return cookieRememberMeManager;
    }

    @Bean
    public SimpleCookie simpleCookie(){
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setMaxAge(7*24*60*60);
        simpleCookie.setHttpOnly(true);

        return simpleCookie;
    }

    /**
     * 身份认证realm
     * @return
     */
    @Bean
    public ShiroDbRealm myShiroRealm() {
        ShiroDbRealm myShiroRealm = new ShiroDbRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 凭证匹配器
     * （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     *  所以我们需要修改下doGetAuthenticationInfo中的代码;
     * ）
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("MD5"); // 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1024); // 散列的次数，比如散列两次，相当于 md5(md5(""));

        return hashedCredentialsMatcher;
    }

    /**
     * Shiro生命周期处理器:
     * 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm)
     * 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     *  开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全
     *  逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 * @return
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * shiro注解支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     *  Shiro 过滤器
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();

        // SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 登陆页面
        shiroFilterFactoryBean.setLoginUrl("/sys/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/sys/");
        // 未授权界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/global/error");


        /**
         * 配置shiro拦截器链
         *
         * anon  不需要认证
         * authc 需要认证
         * user  验证通过或RememberMe登录的都可以
         *
         * 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的
         *
         * 顺序从上到下,优先级依次降低
         *
         */
        // 拦截器.
        Map<String,String> hashMap = new LinkedHashMap<String,String>();

        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        hashMap.put("/webjars/**", "anon");
        hashMap.put("/druid/**", "anon");
        hashMap.put("/**/*.js", "anon");
        hashMap.put("/**/*.css", "anon");
        hashMap.put("/asserts/fonts/**", "anon");
        hashMap.put("/asserts/img/**", "anon");
        hashMap.put("/asserts/media/**", "anon");
        hashMap.put("/plugins/**", "anon");
        hashMap.put("/swagger-ui.html", "anon");
        hashMap.put("/favicon.ico", "anon");
        hashMap.put("/kaptcha/**", "anon");
        hashMap.put("/wechat/**", "anon");
        hashMap.put("/test/**", "anon");
        hashMap.put("/**", "user");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}